# -*- coding: utf-8 -*-
import cv2
import numpy as np


class Vision():
    def __init__(self, video_proxy):
        self.video_proxy = video_proxy
        # camera_id = 1  # 使用默认的摄像头
        # resolution = 2  # 640x480分辨率
        # color_space = 13  # RGB颜色空间
        # fps = 20  # 帧速率
        # # 修正参数传递方式
        self.video_client = self.video_proxy.subscribeCamera("demo", 1, 2, 13, 20)

    def get_frame(self):
        # 获取摄像头图像
        image = self.video_proxy.getImageRemote(self.video_client)
        # 解析图像数据
        width = image[0]
        height = image[1]
        image_data = image[6]
        # print image_data
        # 将图像数据转换为OpenCV图像
        frame = np.frombuffer(image_data, dtype=np.uint8).reshape((height, width, 3))
        return frame

    def redball_det(self):
        # 扩大红色阈值范围
        red_lower = np.array([0, 0, 100])  # 下限阈
        red_upper = np.array([100, 60, 240])  # 上限阈值
        try:
            frame = self.get_frame()
            # 使用颜色阈值过滤出红色球体
            mask = cv2.inRange(frame, red_lower, red_upper)

            # 中值滤波处理
            mask = cv2.medianBlur(mask, 5)

            # 找到红球的轮廓
            contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]

            for contour in contours:
                area = cv2.contourArea(contour)
                if 1000 <= area <= 8000:  # 过滤轮廓面积在1000到10000之间的轮廓
                    # 检测到红球，停止位置真

                    print "检测到红球。"
                    return True
        except Exception as e:
            print e
            self.error_handling()

    def line_det(self):
        '''
        识别直线，并筛选出候选的直线。
        :return: lines ,hough变换检测到的结果，直接返回
        :rtype:list
        '''
        try:
            frameArray = self.get_frame()
            # 转变灰度值
            image_gray = cv2.cvtColor(frameArray, cv2.COLOR_BGR2GRAY)
            # 转换二值图像
            retval, image_thre = cv2.threshold(image_gray, 200, 255, cv2.THRESH_BINARY)
            # 高斯滤波
            image_thre = cv2.GaussianBlur(image_thre, (3, 3), 1)
            # 形态学变换
            kernel = np.ones((3, 3), np.uint8)
            image_thre = cv2.morphologyEx(image_thre, cv2.MORPH_CLOSE, kernel)
            image_thre = cv2.morphologyEx(image_thre, cv2.MORPH_OPEN, kernel)
            # 轮廓检测
            contours = cv2.Canny(image_thre, 190, 300)
            lines_ci = cv2.HoughLines(contours, 1, np.pi / 180, 175)
            # print lines_ci
            return lines_ci
        except Exception as e:
            print e
            self.error_handling()

    def non_maximum_suppression(self, lines):
        '''
        非极大值抑制算法，
         NMS，单独封装函数，输入检测到的lines，-->lines:list[theta1,theta2...]
        :param lines: hough检测到的lines列表，直接输入。
        :type lines: list
        :return: nms_lines 包含经过非极大抑制后的直线角度值。
        :rtype: list
        '''
        return nms_lines

    def line_to_control(self):
        '''
        对NMS之后的直线进行处理，并得到最终的control修正角度。
        1. 将直线分为横竖两类
        2. 对竖线做最终，无竖线
        :return: control， 返回控制值
        :rtype: float
        '''
        # control=0.0
        lines = self.line_det()

        # 未检测到直线，直接返回空值。
        if lines == []:
            print  "未检测到直线。"
            return None
        # 过NMS
        lines = self.non_maximum_suppression(lines)

        def line_classifcation(lines):
            lines_dic = {'vertical': [],
                         'horizontal': []}
            if lines is not None:
                for line in lines:
                    _, theta = line[0]
                    if 0 <= theta < np.pi / 4:
                        # 直接计算和，丢弃距离
                        lines_dic['vertical'].append(line[0][1])
                        # 记录（弃用
                        # self.lines["vertical"].append(line[0])
                    elif 3 * np.pi / 4 < theta <= np.pi:
                        # 将钝角，转换为和180度的夹角（即x轴的夹角）
                        lines_dic['vertical'].append(-np.pi + line[0][1])
                        # 记录（弃用
                        # self.lines["vertical"].append(line[0])
                    elif np.pi / 4 <= theta <= 3 * np.pi / 4:
                        lines_dic['horizontal'].append(line[0][1])
            return lines_dic

        lines = line_classifcation(lines)
        if len(lines['vertical']) == 2:
            control = np.sum(lines['vertical'])
        elif len(lines['horizontal']) == 1:
            control = np.sum(lines['horizontal'])
        else:
            control = None

        print "本次检测结果,竖线" +len(lines['vertical']) +"条," + +"横线" +len(lines['horizontal']) +"条，最终修正角度" + control
        return control

    def error_handling(self):
        self.video_proxy.releaseImage(self.video_client)
        self.video_proxy.unsubscribe(self.video_client)

    def __del__(self):
        self.error_handling()


'''
        根据直线修正方向
        1. 这里hough检测到的角度，就是需要修正的角度。比如，直线负斜率，则hough变换得到的角度（再计算和pi的差值之后），
        但是还要考虑直线，默认的一个倾角（即机器人直走，但是视角中，直线仍然有一定倾角
        解决办法：直接计算两个直线角度之和。若相等，即角度=0，则为正中心。否则向另一边偏转即可。

        问题：如何保证在，vertical只检测到一条线的时候做特判
        1. 提高检测性能，尽量避免
        2. 做特判，若单边，则用单边检测的方式。
            a. 直接舍弃单边的检测结果，此次不修正
            b.
                暂时思路
        1. 检测结果，过NMS
        2. 堆NMS结果，分类，horizontal和vertical两种
        3. 堆vertical，只有，只检测到两个线，修正，其他都直接丢弃
        3. 无vertical，则堆horizontal，做转向。
'''
